#include "vm.h"

static int turn = 0;
int run_world(dot* world, int grid_size) {
	int total_world_size = grid_size * grid_size;
	int i, x, y;
	turn++;

	// Go through entire grid looking for AI.
	for (i = 0; i < total_world_size; i++) {
		if (world[i].data != NULL && world[i].data->last_turn < turn) {
			world[i].data->last_turn = turn;

			// Get coordinates
			x = i % grid_size;
			y = i / grid_size;
			run_commands(world, x, y, grid_size);
		}
	}
	return 0;
};

/*
 * Goes through the commands of a given dot AI.
 */
int run_commands(dot* world, int x, int y, int grid_size) {
	ai_data my_ai = *(world[x + (y * grid_size)].data);
	int i;

	if (my_ai.energy <= 0) {
		// TODO: kill AI
	}

	my_ai.energy--;
	for (i = 0; i < my_ai.com_count; i++) {

		// Special command classes
		if (COM_CLASS(my_ai.commands[i]) == COM_MOV) {
			my_ai.flag = mov_in_dir(world, COM_DIRECTION(my_ai.commands[i]), grid_size, x, y);
			continue;
		}
		if (COM_CLASS(my_ai.commands[i]) == COM_LOOK) {
			my_ai.last_look = look_for(world, DEFAULT_DISTANCE, x, y, grid_size, obj_to_type(COM_OBJ(my_ai.commands[i])));
			continue;
		}

		switch (my_ai.commands[i]) {
		case COM_MOV_IN_DIR:
			if (my_ai.last_look != -1) {
				mov_in_dir(world, my_ai.last_look, grid_size, x, y);
			}
			break;

		case COM_MOV_OPPOSITE:
			if (my_ai.last_look != -1) {
				mov_in_dir(world, get_opposite_direction(my_ai.last_look), grid_size, x, y);
			}
			break;

		case COM_SPLIT:
			
			break;

		case BLANK_COMMAND:
			break;
		}
	}
}

/*
 * Looks for the given type at a given distance (gets the current location to look from).
 * Returns the direction of the first item found (closest, clockwise).
 * If none found, returns -1.
 */
int look_for(dot* world, int distance, int x, int y, int grid_size, int to_look_for) {
	int i;
	for (i = 0; i < distance; i++) {
		if (world[((x + 1) % grid_size) + (y * grid_size)].type[0] == to_look_for) {
			return RIGHT;
		}
		if (world[((x - 1) % grid_size) + (y * grid_size)].type[0] == to_look_for) {
			return LEFT;
		}
		if (world[x + (((y + 1) % grid_size) * grid_size)].type[0] == to_look_for) {
			return DOWN;
		}
		if (world[x + (((y - 1) % grid_size) * grid_size)].type[0] == to_look_for) {
			return UP;
		}
	}
	return -1;
}

/*
 * Moves the dot in the direction of a directional command.
 */
int mov_in_dir(dot* world, int direction, int grid_size, int x, int y) {
	switch (direction) {
	case UP:
		return attempt_mov(world, x + (y * grid_size), x + ((y - 1 % grid_size) * grid_size));
	case DOWN:
		return attempt_mov(world, x + (y * grid_size), x + ((y + 1 % grid_size) * grid_size));
	case RIGHT:
		return attempt_mov(world, x + (y * grid_size), ((x + 1) % grid_size) + (y * grid_size));
	case LEFT:
		return attempt_mov(world, x + (y * grid_size), ((x - 1) % grid_size) + (y * grid_size));
	}
	return -1;
}

/*
 * Attempts to move a dot from the old location to the new location.
 * Can move only if the new location is empty or has food on it.
 * Returns TRUE / FALSE according to whether it moved.
 */
int attempt_mov(dot* world, int old_location, int new_location) {
	switch (world[new_location].type[0]) {
		case BLANK_DOT:
			replace_dot(&world[old_location], &world[new_location]);
			clear_dot(&world[old_location]);
			return TRUE;

		case FOOD_DOT:
			replace_dot(&world[old_location], &world[new_location]);
			clear_dot(&world[old_location]);
			if (world[new_location].data != NULL) {
				world[new_location].data->energy++;
			}
			return TRUE;

		case AI_DOT:
			return FALSE;
			// Derp?
	}

	return -1;
}